﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;

using System.Data;
using System.Data.Entity;
using System.Net;
using System.Web.Mvc;
using OnlineMediaAnalyzer.Models;
using System.Xml;
using System.Globalization;
using System.IO;
using System.Collections.Specialized;
using System.Text;
using System.Text.RegularExpressions;
using System.Threading;
using Quartz;

namespace OnlineMediaAnalyzer
{
    public class ScheduledTask : IJob
    {

        public void Execute(IJobExecutionContext context)
        {
            Process();
        }

        public static void Process()
        {
            List<Category> newCategories = new List<Category>();
            List<Article> newArticles = new List<Article>();
            List<PartyMember> partyMembers;
            List<PartyKeyword> partyKeywords;
            List<Feed> feeds;

            using (OnlineMediaAnalyzerContext db = new OnlineMediaAnalyzerContext())
            {
                partyMembers = db.PartyMembers.ToList();
                partyKeywords = db.PartyKeywords.ToList();
                feeds = db.Feeds.ToList();

                foreach (Feed feed in feeds) {
                    ProcessFeed(feed, db, newCategories, newArticles, partyMembers, partyKeywords);
                }

                db.SaveChanges();
            }

            AnalysisTask.Process(newArticles, feeds);
        }

        public static void ProcessFeed(Models.Feed feed, OnlineMediaAnalyzerContext db, List<Category> newCategories, List<Article> newArticles, List<PartyMember> partyMembers, List<PartyKeyword> partyKeywords)
        {
            XmlDocument xmlDoc = new XmlDocument();
            try
            {
                xmlDoc.Load(feed.Url);

                RSSMap map = db.RSSMaps.Find(feed.RSSMapID);
                if (map != null)
                {

                    XmlNodeList articles = xmlDoc.GetElementsByTagName(map.ItemMapping);
                    string[] idSeparators = new string[] {"id="};
                    foreach (XmlNode article in articles)
                    {
                        string guid = findElementData(article, map.GuidMapping);
                        if (guid.Contains(idSeparators[0]))
                        {
                            guid = guid.Split(idSeparators, StringSplitOptions.None)[0];
                        }
                        guid = Truncate(guid, 255);

                        bool exists = db.Articles.Any(art => art.Identification == guid);
                        foreach (Article newa in newArticles) {
                            if (newa.Identification == guid)
                            {
                                exists = true;
                                break;
                            }
                        }
                        if (exists) break;

                        string title = FormatText(findElementData(article, map.TitleMapping));
                        string url = findElementData(article, map.UrlMapping);
                        string description = FormatText(findElementData(article, map.DescriptionMapping));
                        string author = FormatText(findElementData(article, map.AuthorMapping));
                        string time = findElementData(article, map.TimeMapping);

                        if (author.Length == 0) { author = feed.Name; }

                        Article a = db.Articles.Create();
                        a.Title = Truncate(title, 255);
                        a.Identification = guid;
                        a.Url = Truncate(url, 255);
                        a.Description = Truncate(description, 1023);
                        a.Author = Truncate(author, 63);
                        a.Time = parseTime(time);
                        a.FetchingTime = DateTime.Now;
                        a.Feed = feed;
                        a.Categories = new List<Category>();

                        bool isPolitical = IsPolitical(a, partyMembers, partyKeywords);
                        if (isPolitical)
                        {
                            a.Sentiment = RequestSentiment(a);
                        }

                        db.Articles.Add(a);
                        newArticles.Add(a);

                        List<string> categoryNames = findElementsData(article, map.CategoryMapping);
                        foreach (string categoryName in categoryNames)
                        {
                            Category category = null;
                            foreach (Category cat in newCategories)
                            {
                                if (cat.Name == categoryName)
                                {
                                    category = cat;
                                    break;
                                }
                            }
                            if (category == null)
                            {
                                if (db.Categories.Any(cat => cat.Name == categoryName))
                                {
                                    category = db.Categories.Where(cat => cat.Name == categoryName).FirstOrDefault();
                                }
                                else
                                {
                                    category = db.Categories.Create();
                                    category.Name = categoryName;
                                    category.Feed = feed;
                                    category.Url = feed.Url;

                                    db.Categories.Add(category);
                                    newCategories.Add(category);
                                }
                            }
                            //Add this category to article
                            a.Categories.Add(category);
                        }
                    }
                }
                else
                {
                    System.Diagnostics.Trace.TraceError("Corresponding RSS map not found with id:" + feed.RSSMapID + " for feed:" + feed.Name);
                }
            }
            catch (FileNotFoundException)
            {
                System.Diagnostics.Trace.TraceError("No feed found from url: " + feed.Url + " in feed: " + feed.Name);
            }
            //Proxy and stuff: http://www.aspsnippets.com/Articles/Fetch-and-Display-RSS-Feeds-using-ASP.Net.aspx
        }

        public static string FormatText(string input)
        {
            string result = Regex.Replace(input, @"<[^>]*>", String.Empty);
            result = System.Web.HttpUtility.HtmlDecode(result);
            return result;
        }

        public static bool IsPolitical(Article article, List<PartyMember> partyMembers, List<PartyKeyword> partyKeywords)
        {
            string title = article.Title;
            string description = article.Description;
            string lowerTitle = title.ToLower();
            string lowerDescription = description.ToLower();


            foreach (PartyMember member in partyMembers)
            {
                if (title.Contains(member.Name) || description.Contains(member.Name)) {
                    return true;
                }
            }

            foreach (PartyKeyword keyword in partyKeywords)
            {
                string lowerKeyword = keyword.Keyword.ToLower();
                if (lowerTitle.Contains(lowerKeyword) || lowerDescription.Contains(lowerKeyword))
                {
                    return true;
                }
            }
            return false;
        }

        public static Sentiment? RequestSentiment(Article article)
        {
            string url = "http://peeter.eki.ee:5000/valence/color";

            using (var wb = new WebClient())
            {
                
                var data = new NameValueCollection();
                data["dataonly"] = "2";
                data["text"] = article.Title;

                Thread.Sleep(100);
                try
                {
                    string response = Encoding.ASCII.GetString(wb.UploadValues(url, "POST", data));
                    switch (response)
                    {
                        case "positiivne":
                            return Sentiment.positive;
                        case "negatiivne":
                            return Sentiment.negative;
                        case "ekstreemne":
                            return Sentiment.extreme;
                        case "neutraalne":
                            return Sentiment.neutral;
                        case "vastuoluline":
                            return Sentiment.conflicting;
                        default:
                            return null;
                    }
                }
                catch (System.Net.WebException)
                {

                }
                return Sentiment.neutral;
            }
        }

        public static DateTime parseTime(string time)
        {
            DateTime datetime;
            //String format = "ddd, dd MMM yyyy HH:mm:ss zzz";
            if (DateTime.TryParse(time, out datetime))
            {
                datetime = Convert.ToDateTime(datetime);
            } else {
                datetime = DateTime.Now;
            }
            return datetime;
        }

        public static string findElementData(XmlNode parentNode, string tagName)
        {
            string result = "";

            XmlNode node = (parentNode as XmlElement).SelectSingleNode("*[local-name()='"+tagName+"']");

            if (node != null)
            {
                if (node.HasChildNodes && node.FirstChild is XmlCDataSection)
                {
                    result = (node.FirstChild as XmlCDataSection).Data;
                }
                else if (node is XmlCDataSection)
                {
                    result = (node as XmlCDataSection).Data;
                }
                else
                {
                    result = node.InnerText;
                }
            }
            return result;
        }

        public static List<string> findElementsData(XmlNode parentNode, string tagName)
        {
            List<string> result = new List<string>();

            XmlNodeList nodes = (parentNode as XmlElement).SelectNodes("*[local-name()='" + tagName + "']");
            foreach (XmlNode node in nodes) {
                if (node != null)
                {
                    if (node.HasChildNodes && node.FirstChild is XmlCDataSection)
                    {
                        result.Add((node.FirstChild as XmlCDataSection).Data);
                    }
                    else if (node is XmlCDataSection)
                    {
                        result.Add((node as XmlCDataSection).Data);
                    }
                    else
                    {
                        result.Add(node.InnerText);
                    }
                }
            }
            return result;
        }

        public static string Truncate(string value, int maxLength)
        {
            if (string.IsNullOrEmpty(value)) return value;
            return value.Length <= maxLength ? value : value.Substring(0, maxLength);
        }

    }
}